package cn.yeziji.forum.factory.websocket.parts;

import cn.yeziji.forum.bo.chatroom.ChatPrivateMsgBO;
import cn.yeziji.forum.common.key.WebsocketCacheKey;
import cn.yeziji.forum.common.websocket.Chatroom;
import cn.yeziji.forum.converter.ChatroomConverter;
import cn.yeziji.forum.dao.intf.ForumChatroomDao;
import cn.yeziji.forum.dao.intf.ForumUserDao;
import cn.yeziji.forum.entity.ForumChatroomEntity;
import cn.yeziji.forum.entity.ForumUserEntity;
import cn.yeziji.forum.factory.websocket.WebsocketOperationAbstract;
import cn.yeziji.forum.factory.websocket.WebsocketOperationInterface;
import cn.yeziji.forum.factory.websocket.enums.WebsocketFactoryOption;
import cn.yeziji.forum.mapper.ForumUserMapper;
import cn.yeziji.forum.service.websocket.WebsocketService;
import cn.yeziji.forum.utils.CacheUtils;
import cn.yeziji.forum.utils.ListsCollect;
import cn.yeziji.forum.utils.SpringUtils;
import cn.yeziji.forum.vo.chatroom.RecordsVO;
import com.github.benmanes.caffeine.cache.Cache;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * chatroom 操作流程
 *
 * @author gzkemays
 * @since 2022/3/27 14:25
 * @deprecated to netty
 */
@Slf4j
public class ChatroomPart extends WebsocketOperationAbstract<Chatroom>
    implements WebsocketOperationInterface {
  final String HEART_KEY = "heart";
  final String ALL_KEY = "publishChat";
  final Map<String, List<String>> keys = new ConcurrentHashMap<>();

  private ForumUserMapper getUserMapper() {
    return SpringUtils.getBean(ForumUserMapper.class);
  }

  private ForumUserDao getUserDao() {
    return SpringUtils.getBean(ForumUserDao.class);
  }

  public ChatroomPart(Object data) {
    super(data);
  }

  public ChatroomPart(Object data, Object additional) {
    super(data, additional);
  }

  @Override
  public Object operation(WebsocketFactoryOption option) {
    return invoke(getClass(), option);
  }

  /**
   * 默认操作为基本聊天
   *
   * @return 聊天构造信息 {@link ChatPrivateMsgBO}
   */
  @Override
  protected Object defaultMethod() {
    String paramsKey = params.getKey();
    String msg = params.getMsg();
    if (Objects.nonNull(msg)) {
      ForumUserDao userDao = getUserDao();
      params.buildChatroom();
      params.setCreateTime(new Date());
      ChatPrivateMsgBO toAnFrom = userDao.getByToAndFromUserMsg(params.getTo(), params.getFrom());
      // 这里如果 websocket 链接是可以相互通信的，即 from --> to 时可以进行传递,则将 updateTime 进行更新,作为实时聊天记录的依据.
      Cache<String, String> userCache = getUserCache();
      String to = params.getTo();
      // 查看用户是否在线
      ForumUserEntity userEntity = userDao.selectUserByNickName(to);
      String ifPresent = userCache.getIfPresent(userEntity.getId().toString());
      if (userCache.getIfPresent(to) != null) {
        // 发送用户在相互对话时将 updateTime 设为当前发送时间.
        params.setUpdateTime(new Date());
      } else if (ifPresent != null) {
        // 用户在线但没有进入聊天室，那么 toOnline 为 true，toOnlineKey 是当前用户的 token.
        toAnFrom.setToOnlineKey(ifPresent);
      }
      toAnFrom.buildByChatroom(params);
      send(toAnFrom);
      return toAnFrom;
    }
    if (paramsKey.equals(HEART_KEY)) {
      send(params);
      return params;
    }
    return null;
  }
  /** redis 过期操作 */
  public void redisExpiredHandler() {
    CacheUtils cache = getDefaultCache();
    Cache<String, String> userCache = getUserCache();
    String key = additional.toString();
    ForumChatroomDao chatroomDao = getChatroomDao();
    List<Object> blurHashKey = cache.getBlurHashKey(key);
    Set<Chatroom> chatrooms = new HashSet<>();
    for (Object obj : blurHashKey) {
      if (obj instanceof Chatroom) {
        Chatroom chatroom = (Chatroom) obj;
        // 如果处于对话状态或者通信来源于 user-records 时对 updateTime 进行设置。
        if (userCache.getIfPresent(chatroom.getTo()) != null
            || params.getKey().equals(Chatroom.USER_RECORDS)) {
          chatroom.setUpdateTime(new Date());
        }
        chatrooms.add(chatroom);
        cache.blurRemoveHash(key, null);
      }
    }
    if (!chatrooms.isEmpty()) {
      chatroomDao.batchSave(ChatroomConverter.INSTANCE.wbMsgConvertToEntities(chatrooms));
    }
  }

  /**
   * 获取聊天记录
   *
   * @return 聊天记录
   */
  public Set<RecordsVO> records() {
    RedisTemplate<String, Object> redisTemplate = getDefaultCache().getRedisTemplate();
    String paramsKey = params.getKey();
    if (paramsKey.equals(Chatroom.PRIVATE)) {
      if (params.getChatroom() == null) {
        params.buildChatroom();
      }
      // 根据聊天室获取聊天记录
      String chatroom = params.getChatroom();
      List<ForumChatroomEntity> chatroomRecords =
          getChatroomDao().getChatroomRecords(chatroom, true);
      // 获取 redis 中的聊天记录
      /*      Map<Object, Object> entries =
          redisTemplate.opsForHash().entries(WebsocketCacheKey.privateChatroom(chatroom));
      for (Map.Entry<Object, Object> entry : entries.entrySet()) {
        Object value = entry.getValue();
        if (value instanceof Chatroom) {
          chatroomRecords.add(ChatroomConverter.INSTANCE.wbMsgConvertToEntity((Chatroom) value));
        }
      }*/

      // 获取私聊聊天室中的用户信息
      ChatPrivateMsgBO toAnFrom =
          getUserDao().getByToAndFromUserMsg(params.getTo(), params.getFrom());
      String finalToAvatar = toAnFrom.getToAvatar();
      String finalFromAvatar = toAnFrom.getFromAvatar();
      // 组建聊天记录列表
      LinkedHashSet<RecordsVO> collect =
          chatroomRecords.stream()
              .sorted(Comparator.comparing(ForumChatroomEntity::getCreateTime))
              .map(
                  entity -> {
                    RecordsVO recordsVO =
                        ChatroomConverter.INSTANCE.entityConvertToRecordsVo(entity);
                    recordsVO.setToAvatar(finalToAvatar);
                    recordsVO.setFromAvatar(finalFromAvatar);
                    return recordsVO;
                  })
              .collect(Collectors.toCollection(LinkedHashSet::new));
      send(params.getFrom(), collect, JSON_ARRAY);
      return collect;
    } else if (paramsKey.equals(Chatroom.ALL)) {
      params.buildChatroom();
      // FIXME：批量查询用户信息
      List<ForumChatroomEntity> chatroomRecords =
          getChatroomDao().getChatroomRecords(params.getChatroom(), true);
      List<RecordsVO> recordsVOS =
          ChatroomConverter.INSTANCE.entitiesConvertToRecordsVos(chatroomRecords);
      // 通信 user cache
      Set<Map.Entry<@NonNull String, @NonNull String>> entries = getUserCache().asMap().entrySet();
      for (Map.Entry<String, String> entry : entries) {
        send(entry.getKey(), recordsVOS, JSON_ARRAY);
      }
    }
    return null;
  }

  /** 关闭链接 */
  public Chatroom close() {
    params.buildChatroom();
    this.additional = WebsocketCacheKey.privateChatroom(params.getChatroom());
    redisExpiredHandler();
    params.setMsg(params.getFrom() + "退出");
    if (getWebsocketService().getConcurrentMap().containsKey(params.getTo())) {
      send(params.getTo(), params, JSON);
    }
    return params;
  }

  /** 公共聊天室 */
  public void publishChat() {
    params.buildChatroom();
    Objects.requireNonNull(params.getFrom());
    String from = params.getFrom();
    String avatar = params.getAvatar();
    String ifPresent = getUserCache().getIfPresent(from);
    List<String> all = ListsCollect.of(this.keys.get(ALL_KEY)).orElseEmpty();
    // 如果为空则意味着该用户没有登记入聊天室
    if (ifPresent == null || all.isEmpty() || !all.contains(from)) {
      getUserCache().put(from, avatar == null ? from : avatar);
      all.add(from);
    }
    // 不等于空时推送消息
    for (String user : all) {
      send(ALL_KEY + "-" + user, params.getMsg(), JSON);
    }
    this.keys.put(ALL_KEY, all);
  }

  public void publishClose() {
    params.buildChatroom();
    Objects.requireNonNull(params.getFrom());
    String from = params.getFrom();
    List<String> keys = this.keys.get(ALL_KEY);
    if (!keys.isEmpty()) {
      keys.remove(from);
    }
    // TODO: cache save ?
  }

  /**
   * 信息对话
   *
   * <ul>
   *   <li>如果 {@link WebsocketService#getConcurrentMap()} 中涵盖发送对象的信息，属于相互对话
   *   <li>如果已登录则视为留言，发给 online key
   * </ul>
   *
   * @param data
   */
  private void send(Object data) {
    ConcurrentHashMap<String, WebsocketService> concurrentMap =
        getWebsocketService().getConcurrentMap();
    String to = params.getTo();
    if (concurrentMap.containsKey(to)) {
      send(to, data, JSON);
    } else {
      if (data instanceof ChatPrivateMsgBO) {
        String toOnlineKey = ((ChatPrivateMsgBO) data).getToOnlineKey();
        if (toOnlineKey != null) {
          // 发送至 online 告诉有新的未读
          send(toOnlineKey, data, JSON);
        } else {
          // 不在线则将数据进行缓存
          saveChatroomCache(params);
        }
      }
    }
  }
}
